using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Styx;
using Styx.Combat.CombatRoutine;
using Styx.Helpers;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using TCRestoDruid.UI;

namespace TCRestoDruid
{
    class TCRestoDruidCombatClass : CombatRoutine
    {
        private WoWUnit lastTarget = null;
        private WoWPlayer tank = null;
        private WoWPlayer lastTank = null;
        private bool firstInnervate = true;
        private string lastSpell = null;

        //START OF CONSTANTS ==============================
        private const string TANK_CHANGE = "Tank changed to";
        private const string AOE_LABEL = "[AOE]";
        private const string SOS_LABEL = "[SOS]";
        private const string TANK_HEALING_LABEL = "[TANK HEALING]";
        private const string HEALING_LABEL = "[HEALING]";
        private const string CLEARCASTING_LABEL = "[CLEARCASTING]";

        private const int FULL_HP = 100;

        //START OF SPELLS AND AURAS ==============================
        private const string AUTO_ATTACK = "Auto Attack";

        private const string INNERVATE = "Innervate";
        private const string BARKSKIN = "Barkskin";
        private const string NATURE_GRASP = "Nature's Grasp";
        private const string TREE_OF_LIFE = "Tree of Life";

        private const string LIFEBLOOM = "Lifebloom";
        private const string REJUVENATION = "Rejuvenation";
        private const string NOURISH = "Nourish";
        private const string REGROWTH = "Regrowth";
        private const string HEALING_TOUCH = "Healing Touch";
        private const string SWIFTMEND = "Swiftmend";

        private const string WILD_GROWTH = "Wild Growth";
        private const string TRANQUILITY = "Tranquility";

        private const string NATURE_SWIFTNESS = "Nature's Swiftness";
        private const string HARMONY = "Harmony";
        private const string CLEARCASTING = "Clearcasting";

        private const string MARK_OF_THE_WILD = "Mark of the Wild";
        private const string BLESSING_OF_KINGS = "Blessing of Kings";

        private const string REBIRTH = "Rebirth";
        private const string REVIVE = "Revive";

        private const string REMOVE_CORRUPTION = "Remove Corruption";

        private const string ENTANGLING_ROOTS = "Entangling Roots";
        private const string STARFIRE = "Starfire";
        private const string WRATH = "Wrath";
        private const string MOONFIRE = "Moonfire";
        private const string INSECT_SWARM = "Insect Swarm";

        private const string DRINK = "Drink";
        private const string FOOD = "Food";
        //END OF SPELLS AND AURAS ==============================

        //START OF DEBUFFS ==============================
        private const string TOXIC_TORMENT = "Toxic Torment";
        private const string BURNING_BLOOD = "Burning Blood";
        private const string FROSTBURN_FORMULA = "Frostburn Formula";
        //END OF DEBUFFS ==============================

        //END OF CONSTANTS ==============================

        public override void Pulse()
        {
            if (!CheckUnit(tank) || (CheckUnit(tank) && (tank.IsMe || !tank.InLineOfSight || tank.Distance2D > 40)))
            {
                tank = GetTank();

                if (tank == null) tank = Me;

                if (lastTank == null || lastTank.Guid != tank.Guid)
                {
                    lastTank = tank;
                    LogActivity(TANK_CHANGE, tank.Class.ToString(), "with Maximum Health", tank.MaxHealth.ToString());
                }
            }

            if (CheckUnit(Me) && !Me.Combat)
            {
                firstInnervate = true;

                Revive();

                if (CheckUnit(tank) && tank.Combat)
                {
                    TankHealing();
                }
            }
        }

        public override void Initialize()
        {
            Blacklist.Flush();
        }

        private bool CheckUnit(WoWUnit unit)
        {
            return unit != null && unit.IsValid && unit.IsAlive;
        }

        public override bool WantButton
        {
            get
            {
                return true;
            }
        }

        private static Thread Thread { get; set; }
        private TCRestoDruidSettingsForm settingsWindow;

        public override void OnButtonPress()
        {
            Thread = new Thread(OpenGui) { IsBackground = true, Name = "UIThread" }; Thread.Start();
        }
        public void OpenGui()
        {
            settingsWindow = new TCRestoDruidSettingsForm();
            settingsWindow.ShowDialog();
        }

        public override bool NeedHeal
        {
            get
            {
                return CheckUnit(GetHealTarget()) && CheckUnit(Me) && !Me.Mounted && ((Me.IsInInstance && TCRestoDruidSettings.Instance.OnlyHealInstances) || !TCRestoDruidSettings.Instance.OnlyHealInstances);
            }
        }

        public override void Heal()
        {
            if (GCD) return;
            if (Rebirth()) return;
            if (Cleansing()) return;
            if (Self()) return;
            if (AoE()) return;
            if (SoS()) return;
            if (TankHealing()) return;
            if (Clearcast()) return;
            if (Healing()) return;
        }

        public override void Combat()
        {
            if (CheckUnit(Me) && !Me.IsInInstance)
            {
                WoWUnit target = GetEnemyTarget();

                if (CheckUnit(target))
                {
                    target.Target();

                    if (!Me.IsFacing(target))
                    {
                        target.Face();
                        WoWMovement.MoveStop();
                    }

                    double hp = Math.Round(target.HealthPercent, 2);

                    if (!Me.IsAutoAttacking)
                    {
                        Cast(AUTO_ATTACK);
                    }
                    
                    if (!isAuraActive(INSECT_SWARM, target) && !isAuraActive(MOONFIRE, target) && CanCast(STARFIRE, target) && lastSpell != STARFIRE)
                    {
                        Cast(STARFIRE, target);
                        LogActivity(STARFIRE, (target.IsPlayer ? target.Class.ToString() : target.Name), hp.ToString());
                        while (GCD) { }
                    }

                    if (!isAuraActive(INSECT_SWARM, target) && CanCast(INSECT_SWARM, target))
                    {
                        Cast(INSECT_SWARM, target);
                        LogActivity(INSECT_SWARM, (target.IsPlayer ? target.Class.ToString() : target.Name), hp.ToString());
                        while (GCD) { }
                    }

                    if (!isAuraActive(MOONFIRE, target) && CanCast(MOONFIRE, target))
                    {
                        Cast(MOONFIRE, target);
                        LogActivity(MOONFIRE, (target.IsPlayer ? target.Class.ToString() : target.Name), hp.ToString());
                        while (GCD) { }
                    }

                    if (isAuraActive(INSECT_SWARM, target) && isAuraActive(MOONFIRE, target) && CanCast(WRATH, target))
                    {
                        Cast(WRATH, target);
                        LogActivity(WRATH, (target.IsPlayer ? target.Class.ToString() : target.Name), hp.ToString());
                        while (GCD) { }
                    }
                }
            }
        }

        public override double? PullDistance
        {
            get
            {
                return 40;
            }
        }

        public override void Pull()
        {
            if (CheckUnit(Me) && !Me.IsInParty && !Me.IsInRaid)
            {
                WoWUnit target = Me.CurrentTarget;

                if (target == null || target.IsFriendly)
                {
                    target = GetEnemyTarget();
                }

                if (CheckUnit(target) && !target.IsFriendly)
                {
                    target.Target();

                    if (!Me.IsFacing(target))
                    {
                        target.Face();
                    }

                    if (CanCast(ENTANGLING_ROOTS, target))
                    {
                        Cast(ENTANGLING_ROOTS, target);
                        LogActivity(ENTANGLING_ROOTS, (target.IsPlayer ? target.Class.ToString() : target.Name), target.HealthPercent.ToString());
                    }
                }
            }
        }

        private List<WoWPlayer> GroupMembers { get { return ObjectManager.GetObjectsOfType<WoWPlayer>(true, true).ToList(); } }

        private List<WoWUnit> EnemyUnits
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWUnit>(true, false)
                    .Where(unit =>
                        !unit.IsFriendly
                        && (unit.Aggro
                           || (unit.IsPlayer && unit.PvpFlagged && Me.IsPvPFlagged))
                        && !unit.IsPet
                        && !unit.Dead
                        && unit.Distance2DSqr <= 15 * 15).ToList();
            }
        }

        private void ReTarget(WoWUnit target)
        {
            if (CheckUnit(Me) && CheckUnit(target) && (!Me.GotTarget || Me.IsTargetingMeOrPet || Me.CurrentTarget.IsFriendly))
            {
                target.Target();
            }
        }

        private WoWPlayer GetTank()
        {
            foreach (WoWPlayer player in GetGroupMembersInRange())
            {
                if (CheckUnit(player) && IsTank(player)) return player;
            }

            return null;
        }

        private string DeUnicodify(string spell)
        {
            StringBuilder sb = new StringBuilder();
            byte[] bytes = Encoding.UTF8.GetBytes(spell);

            foreach (byte b in bytes)
            {
                if (b != 0)
                    sb.Append("\\" + b);
            }

            return sb.ToString();
        }

        private bool IsTank(WoWPlayer player)
        {
            if (CheckUnit(player))
            {
                return Lua.GetReturnValues("return UnitGroupRolesAssigned('" + DeUnicodify(player.Name) + "')").First() == "TANK";
            }

            return false;
        }

        private bool IsHealer(WoWPlayer player)
        {
            if (CheckUnit(player))
            {
                return Lua.GetReturnValues("return UnitGroupRolesAssigned('" + DeUnicodify(player.Name) + "')").First() == "HEALER";
            }

            return false;
        }

        private bool Self()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (Me.HealthPercent < FULL_HP && Me.Combat && CanCast(BARKSKIN) && TCRestoDruidSettings.Instance.UseBarkskin)
            {
                LogActivity(BARKSKIN);
                Cast(BARKSKIN);
                return true;
            }

            if (Me.HealthPercent < FULL_HP && Me.Combat && CanCast(NATURE_GRASP) && TCRestoDruidSettings.Instance.UseNaturesGrasp)
            {
                LogActivity(NATURE_GRASP);
                Cast(NATURE_GRASP);
                return true;
            }

            if ((Me.ManaPercent <= 80 || !firstInnervate) && CanCast(INNERVATE))
            {
                LogActivity(INNERVATE);
                Cast(INNERVATE);
                firstInnervate = false;
                return true;
            }

            if (ShouldCastAoE(TCRestoDruidSettings.Instance.TreePercent) && CanCast(TREE_OF_LIFE) && TCRestoDruidSettings.Instance.UseTree)
            {
                LogActivity(TREE_OF_LIFE);
                Cast(TREE_OF_LIFE);
                return true;
            }

            return false;
        }

        private bool AoE()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (ShouldCastAoE(TCRestoDruidSettings.Instance.TranquilityPercent) && CanCast(TRANQUILITY) && !Me.IsMoving)
            {
                LogActivity(AOE_LABEL, TRANQUILITY);
                Cast(TRANQUILITY);
                return true;
            }

            if (ShouldCastWildGrowth(TCRestoDruidSettings.Instance.WildGrowthPercent) && CanCast(WILD_GROWTH))
            {
                LogActivity(AOE_LABEL, WILD_GROWTH);
                Cast(WILD_GROWTH);
                return true;
            }

            WoWPlayer healTarget = GetHealTarget();

            if (isAuraActive(TREE_OF_LIFE))
            {
                if (CheckUnit(healTarget))
                {
                    double hp = Math.Round(healTarget.HealthPercent, 2);

                    if (healTarget.Distance2D > 40 || !healTarget.InLineOfSight) return false;

                    if (CanCast(REGROWTH, healTarget))
                    {
                        LogActivity(AOE_LABEL, REGROWTH, healTarget.Class.ToString(), hp.ToString());
                        Cast(REGROWTH, healTarget);
                        while (GCD) { }
                        CastSwiftmend(healTarget, hp);
                        return true;
                    }
                }
            }

            return false;
        }

        private bool SoS()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            WoWPlayer healTarget = GetHealTarget();

            if (CheckUnit(healTarget))
            {
                double hp = Math.Round(healTarget.HealthPercent, 2);

                if (healTarget.Distance2D > 40 || !healTarget.InLineOfSight) return false;
                else if (hp <= TCRestoDruidSettings.Instance.SOSHealingPercent)
                {
                    if (!isAuraActive(NATURE_SWIFTNESS) && CanCast(NATURE_SWIFTNESS))
                    {
                        LogActivity(SOS_LABEL, NATURE_SWIFTNESS);
                        Cast(NATURE_SWIFTNESS);
                        while (GCD) { }
                        Cast(HEALING_TOUCH, healTarget);
                        return true;
                    }

                    if (CastSwiftmend(healTarget, hp))
                    {
                        return true;
                    }

                    if (CanCast(REGROWTH, healTarget) && (!Me.IsMoving || isAuraActive(TREE_OF_LIFE)))
                    {
                        LogActivity(SOS_LABEL, REGROWTH, healTarget.Class.ToString(), hp.ToString());
                        Cast(REGROWTH, healTarget);
                        while (GCD) { }
                        CastSwiftmend(healTarget, hp);
                        return true;
                    }

                    if (CanCast(HEALING_TOUCH) && !Me.IsMoving)
                    {
                        LogActivity(SOS_LABEL, HEALING_TOUCH, healTarget.Class.ToString(), hp.ToString());
                        Cast(HEALING_TOUCH, healTarget);
                        return true;
                    }
                }
            }

            return false;
        }

        private bool Healing()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            WoWPlayer healTarget = GetHealTarget();

            if (CheckUnit(healTarget))
            {
                if (healTarget.Distance2D > 40 || !healTarget.InLineOfSight) return false;
                else
                {
                    double hp = Math.Round(healTarget.HealthPercent, 2);

                    if (hp < TCRestoDruidSettings.Instance.SwiftmendPercent && CastSwiftmend(healTarget, hp))
                    {
                        return true;
                    }

                    if (Me.ManaPercent > 50 && hp < TCRestoDruidSettings.Instance.RejuvenationPercent && !isAuraActive(REJUVENATION, healTarget) && CanCast(REJUVENATION, healTarget))
                    {
                        LogActivity(HEALING_LABEL, REJUVENATION, healTarget.Class.ToString(), hp.ToString());
                        Cast(REJUVENATION, healTarget);
                        while (GCD) { }
                        CastSwiftmend(healTarget, hp);
                        return true;
                    }

                    if (hp < TCRestoDruidSettings.Instance.RegrowthPercent && CanCast(REGROWTH, healTarget) && (!Me.IsMoving || isAuraActive(TREE_OF_LIFE)))
                    {
                        LogActivity(HEALING_LABEL, REGROWTH, healTarget.Class.ToString(), hp.ToString());
                        Cast(REGROWTH, healTarget);
                        while (GCD) { }
                        CastSwiftmend(healTarget, hp);
                        return true;
                    }

                    if (hp < TCRestoDruidSettings.Instance.NourishPercent && CanCast(NOURISH, healTarget) && !isAuraActive(NATURE_SWIFTNESS) && !Me.IsMoving)
                    {
                        LogActivity(HEALING_LABEL, NOURISH, healTarget.Class.ToString(), hp.ToString());
                        Cast(NOURISH, healTarget);
                        return true;
                    }
                }
            }

            return false;
        }

        private bool TankHealing()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (CheckUnit(tank))
            {
                if (tank.Distance2D > 40 || !tank.InLineOfSight) return false;

                double hp = Math.Round(tank.HealthPercent, 2);

                if (CastSwiftmend(tank, hp))
                {
                    return true;
                }

                if (hp < FULL_HP && CanCast(REJUVENATION, tank) && !isAuraActive(REJUVENATION, tank))
                {
                    LogActivity(TANK_HEALING_LABEL, REJUVENATION, tank.Class.ToString(), hp.ToString());
                    Cast(REJUVENATION, tank);
                    while (GCD) { }
                    CastSwiftmend(tank, hp);
                    return true;
                }

                if (CanCast(LIFEBLOOM, tank) && (!isAuraActive(LIFEBLOOM, tank) || isAuraActive(LIFEBLOOM, tank) && tank.Auras[LIFEBLOOM].StackCount < TCRestoDruidSettings.Instance.LifebloomCount))
                {
                    LogActivity(TANK_HEALING_LABEL, LIFEBLOOM, tank.Class.ToString(), hp.ToString());
                    Cast(LIFEBLOOM, tank);
                    return true;
                }

                if (CanCast(NOURISH, tank) && isAuraActive(LIFEBLOOM, tank) && tank.Auras[LIFEBLOOM].TimeLeft.TotalSeconds <= 5 && tank.Auras[LIFEBLOOM].TimeLeft.TotalSeconds >= 3 && !Me.IsMoving)
                {
                    LogActivity(TANK_HEALING_LABEL, NOURISH, tank.Class.ToString(), hp.ToString());
                    Cast(NOURISH, tank);
                    return true;
                }

                if (CanCast(LIFEBLOOM, tank) && isAuraActive(LIFEBLOOM, tank) && tank.Auras[LIFEBLOOM].TimeLeft.TotalSeconds < 3)
                {
                    LogActivity(TANK_HEALING_LABEL, LIFEBLOOM, tank.Class.ToString(), hp.ToString());
                    Cast(LIFEBLOOM, tank);
                    return true;
                }
            }

            return false;
        }

        private bool CastSwiftmend(WoWPlayer target, double hp)
        {
            if (!CheckUnit(Me) && !CheckUnit(target))
            {
                return false;
            }

            if (CanCast(SWIFTMEND, target) && (isAuraActive(REJUVENATION, target) || isAuraActive(REGROWTH, target)))
            {
                LogActivity(SWIFTMEND, target.Class.ToString(), hp.ToString());
                Cast(SWIFTMEND, target);
                return true;
            }

            return false;
        }

        private bool Clearcast()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            WoWPlayer healTarget = GetHealTarget();

            if (CheckUnit(healTarget))
            {
                if (healTarget.Distance2D > 40 || !healTarget.InLineOfSight) return false;
                else
                {
                    double hp = Math.Round(healTarget.HealthPercent, 2);

                    if (isAuraActive(CLEARCASTING) && CanCast(HEALING_TOUCH, healTarget) && !Me.IsMoving && lastSpell != HEALING_TOUCH)
                    {
                        LogActivity(CLEARCASTING_LABEL, HEALING_TOUCH, healTarget.Class.ToString(), hp.ToString());
                        Cast(HEALING_TOUCH, healTarget);
                        return true;
                    }
                }
            }

            return false;
        }

        private bool Rebirth()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (TCRestoDruidSettings.Instance.UseRebirth)
            {
                WoWPlayer player = GetRebirthTarget();

                if (CheckUnit(player))
                {
                    if (!Blacklist.Contains(player.Guid) && (IsTank(player) || IsHealer(player)))
                    {
                        if (player.Distance2D > 40 || !player.InLineOfSight) return false;
                        else if (CanCast(REBIRTH, player) && !Me.IsMoving && Me.Combat)
                        {
                            LogActivity(REBIRTH, player.Class.ToString());
                            Blacklist.Add(player, new TimeSpan(0, 0, 5));
                            Cast(REBIRTH, player);
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        private bool Revive()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (TCRestoDruidSettings.Instance.UseRevive)
            {
                foreach (WoWPlayer player in GetReviveTargets())
                {
                    if (!CheckUnit(player) || Blacklist.Contains(player.Guid)) continue;
                    else
                    {
                        if (player.Distance2D > 40 || !player.InLineOfSight) return false;
                        else if (CanCast(REVIVE, player) && !Me.IsMoving)
                        {
                            LogActivity(REVIVE, player.Class.ToString());
                            Blacklist.Add(player, new TimeSpan(0, 0, 15));
                            Cast(REVIVE, player);
                            return true;
                        }
                    }
                }
            }
            
            return false;
        }

        private void Buff()
        {
            if (!CheckUnit(Me))
            {
                return;
            }

            if (!isAuraActive(MARK_OF_THE_WILD) && CanCast(MARK_OF_THE_WILD) && !isAuraActive(BLESSING_OF_KINGS))
            {
                LogActivity(MARK_OF_THE_WILD);
                Cast(MARK_OF_THE_WILD);
            }

            foreach (WoWPlayer player in GetGroupMembersInRange())
            {
                if (!CheckUnit(player) || player.Distance2D > 40 || player.IsGhost || !player.InLineOfSight || Blacklist.Contains(player.Guid)) continue;
                else if (!isAuraActive(BLESSING_OF_KINGS, player) && !isAuraActive(MARK_OF_THE_WILD, player) && CanCast(MARK_OF_THE_WILD, player))
                {
                    LogActivity(MARK_OF_THE_WILD, player.Class.ToString());
                    Blacklist.Add(player, new TimeSpan(0, 0, 15));
                    Cast(MARK_OF_THE_WILD, player);
                }
            }
        }

        private bool Cleansing()
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            if (TCRestoDruidSettings.Instance.UseRemoveCurse)
            {
                WoWPlayer player = GetCleanseTarget();
                WoWPlayer healTarget = GetHealTarget();

                if (CheckUnit(player) && CheckUnit(healTarget))
                {
                    double hp = Math.Round(healTarget.HealthPercent, 2);

                    if (!Blacklist.Contains(player.Guid) && hp > TCRestoDruidSettings.Instance.SOSHealingPercent)
                    {
                        if (player.Distance2D > 40 || !player.InLineOfSight) return false;
                        else if (CanCast(REMOVE_CORRUPTION, player))
                        {
                            LogActivity(REMOVE_CORRUPTION, player.Class.ToString());
                            Blacklist.Add(player, new TimeSpan(0, 0, 2));
                            Cast(REMOVE_CORRUPTION, player);

                            return true;
                        }
                    }
                }
            }

            return false;
        }

        private bool ShouldCastAoE(int threshold)
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            return (Me.IsInParty || Me.IsInRaid) && GetMemberCountAlive() >= 2 && GetMemberCountBelowThreshold(threshold) >= Math.Round(GetMemberCountAlive() * (TCRestoDruidSettings.Instance.PercentOfPlayersAliveInNeedOfHealing / 100.0));
        }

        private bool ShouldCastWildGrowth(int threshold)
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            return (Me.IsInParty || Me.IsInRaid) && GetMemberCountBelowThreshold(threshold) >= 2;
        }

        private bool CanCast(string spell, WoWUnit target)
        {
            if (!CheckUnit(Me) && !CheckUnit(target))
            {
                return false;
            }

            ReTarget(target);
            return SpellManager.CanCast(spell, target);
        }

        private bool CanCast(string spell)
        {
            if (!CheckUnit(Me))
            {
                return false;
            }

            return SpellManager.CanCast(spell);
        }

        private bool Cast(string spell, WoWUnit target)
        {
            if (!CheckUnit(Me) && !CheckUnit(target))
            {
                return false;
            }

            if (SpellManager.Cast(spell, target))
            {
                lastTarget = target;
                lastSpell = spell;
                return true;
            }
            else return false;
        }

        private bool Cast(string spell)
        {
            return Cast(spell, Me);
        }

        private bool NeedsCleanse(WoWPlayer player)
        {
            if (!CheckUnit(player))
            {
                return false;
            }

            foreach (WoWAura a in player.Auras.Values)
            {
                if (a.IsHarmful && Me.ManaPercent > 35)
                {
                    // ZG Exceptions
                    if (a.Name == TOXIC_TORMENT || a.Name == BURNING_BLOOD || a.Name == FROSTBURN_FORMULA) return false;

                    WoWDispelType t = a.Spell.DispelType;
                    if (t == WoWDispelType.Curse || t == WoWDispelType.Magic || t == WoWDispelType.Poison) return true;
                }
            }

            return false;
        }

        private int GetMemberCountBelowThreshold(int threshold)
        {
            return Enumerable.Count((from unit in GroupMembers
                                     orderby unit.HealthPercent ascending
                                     where (unit.IsInMyPartyOrRaid || unit.IsMe)
                                     where !unit.Dead
                                     where !unit.IsGhost
                                     where unit.Distance2D <= 30
                                     where unit.HealthPercent <= threshold
                                     select unit));
        }

        private int GetMemberCountAlive()
        {
            return Enumerable.Count((from unit in GroupMembers
                                     orderby unit.HealthPercent ascending
                                     where (unit.IsInMyPartyOrRaid || unit.IsMe)
                                     where !unit.Dead
                                     where !unit.IsGhost
                                     where unit.Distance2D <= 30
                                     select unit));
        }

        private WoWPlayer GetHealTarget()
        {
            return (from unit in GroupMembers
                    orderby unit.HealthPercent ascending
                    where (unit.IsInMyPartyOrRaid || unit.IsMe)
                    where !unit.Dead
                    where !unit.IsGhost
                    where unit.Distance2D <= 40
                    where unit.HealthPercent < FULL_HP
                    where unit.InLineOfSight
                    where !unit.Auras.ContainsKey("Bloodletting")
                    select unit).FirstOrDefault();
        }

        private IEnumerable<WoWPlayer> GetReviveTargets()
        {
            return (from unit in GroupMembers
                    orderby unit.MaxHealth descending
                    where unit.IsInMyPartyOrRaid
                    where unit.Dead
                    where !unit.IsMe
                    where !unit.IsGhost
                    where unit.Distance2D <= 40
                    where unit.InLineOfSight
                    select unit);
        }

        private WoWPlayer GetRebirthTarget()
        {
            return (from unit in GroupMembers
                    orderby unit.MaxHealth descending
                    where unit.IsInMyPartyOrRaid
                    where unit.Dead
                    where !unit.IsMe
                    where !unit.IsGhost
                    where unit.Distance2D <= 40
                    where unit.InLineOfSight
                    select unit).FirstOrDefault();
        }

        private WoWPlayer GetCleanseTarget()
        {
            return (from unit in GroupMembers
                    orderby unit.HealthPercent ascending
                    where (unit.IsInMyPartyOrRaid || unit.IsMe)
                    where !unit.Dead
                    where !unit.IsGhost
                    where unit.Distance2D <= 40
                    where NeedsCleanse(unit)
                    where unit.InLineOfSight
                    select unit).FirstOrDefault();
        }

        private IEnumerable<WoWPlayer> GetGroupMembersInRange()
        {
            return (from unit in GroupMembers
                    orderby unit.MaxHealth descending
                    where (unit.IsInMyPartyOrRaid || unit.IsMe)
                    where !unit.Dead
                    where !unit.IsGhost
                    where unit.Distance2D <= 40
                    where unit.InLineOfSight
                    select unit);
        }

        private WoWUnit GetEnemyTarget()
        {
            return (from unit in EnemyUnits
                    orderby unit.Distance2D ascending
                    where unit.InLineOfSight
                    select unit).FirstOrDefault();
        }

        private bool isAuraActive(string name)
        {
            return isAuraActive(name, Me);
        }

        private bool isAuraActive(string name, WoWUnit unit)
        {
            if (!CheckUnit(unit))
            {
                return false;
            }

            return unit.Auras.ContainsKey(name);
        }

        private bool IsDrinkingOrEating()
        {
            return isAuraActive(DRINK) || isAuraActive(FOOD);
        }

        public override bool NeedRest
        {
            get
            {
                if (!CheckUnit(Me))
                {
                    return false;
                }

                return (Me.ManaPercent <= TCRestoDruidSettings.Instance.ManaPercent &&
                    !isAuraActive(DRINK)) || (Me.HealthPercent <= TCRestoDruidSettings.Instance.HealthPercent &&
                    !isAuraActive(FOOD)) && !Me.Combat && !Me.IsMoving && !Me.IsCasting && !Me.Dead;
            }
        }

        public override void Rest()
        {
            if (!CheckUnit(Me))
            {
                return;
            }

            if (Me.ManaPercent <= TCRestoDruidSettings.Instance.ManaPercent &&
                    !isAuraActive(DRINK) && !Me.Combat && !Me.IsMoving && !Me.IsCasting && !Me.Dead)
            {
                LogActivity("Drinking");
                Styx.Logic.Common.Rest.Feed();
            }

            if (Me.HealthPercent <= TCRestoDruidSettings.Instance.HealthPercent &&
                !isAuraActive(FOOD) && !Me.Combat && !Me.IsMoving && !Me.IsCasting && !Me.Dead)
            {
                LogActivity("Eating");
                Styx.Logic.Common.Rest.Feed();
            }

            while (IsDrinkingOrEating())
            {
                Thread.Sleep(1000);
            }
        }

        public override bool NeedPullBuffs
        {
            get
            {
                if (!CheckUnit(Me))
                {
                    return false;
                }

                foreach (WoWPlayer player in GroupMembers)
                {
                    if (CheckUnit(player) && !isAuraActive(MARK_OF_THE_WILD, player))
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        public override bool NeedCombatBuffs
        {
            get
            {
                foreach (WoWPlayer player in GroupMembers)
                {
                    if (CheckUnit(player) && !isAuraActive(MARK_OF_THE_WILD, player))
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        public override bool NeedPreCombatBuffs
        {
            get
            {
                foreach (WoWPlayer player in GroupMembers)
                {
                    if (CheckUnit(player) && !isAuraActive(MARK_OF_THE_WILD, player))
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        public override void PullBuff()
        {
            Buff();
        }

        public override void CombatBuff()
        {
            Buff();
        }

        public override void PreCombatBuff()
        {
            Buff();
        }

        private void LogActivity(params string[] words)
        {
            if (TCRestoDruidSettings.Instance.Log)
                Logging.Write(String.Join(" ", words));
        }

        public static Version version = new Version(1, 0, 9);

        public override sealed string Name { get { return "TCRestoDruid v" + version; } }

        public override WoWClass Class { get { return WoWClass.Druid; } }

        private static LocalPlayer Me { get { return ObjectManager.Me; } }

        private static bool GCD { get { StyxWoW.SleepForLagDuration(); return StyxWoW.GlobalCooldown; } }
    }
}